home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Windows 95 API Bible
/
Windows 95 API Bible 3 Disc Set.iso
/
Win32 API Bible Book 3 of 3.iso
/
chapte16
/
midiout.c
< prev
next >
Wrap
C/C++ Source or Header
|
1996-04-29
|
21KB
|
604 lines
#include <windows.h>
#include <stdio.h>
#include "midiout.h"
#if defined (WIN32)
#define IS_WIN32 TRUE
#else
#define IS_WIN32 FALSE
#endif
#define IS_NT IS_WIN32 && (BOOL)(GetVersion() < 0x80000000)
#define IS_WIN32S IS_WIN32 && (BOOL)(!(IS_NT) && (LOBYTE(LOWORD(GetVersion()))<4))
#define IS_WIN95 (BOOL)(!(IS_NT) && !(IS_WIN32S)) && IS_WIN32
HINSTANCE hInst; // current instance
LPCTSTR lpszAppName = "MyApp";
LPCTSTR lpszTitle = "MIDI Output";
BOOL RegisterWin95( CONST WNDCLASS* lpwc );
int APIENTRY WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPTSTR lpCmdLine, int nCmdShow)
{
MSG msg;
HWND hWnd;
WNDCLASS wc;
wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
wc.lpfnWndProc = (WNDPROC)WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = hInstance;
wc.hIcon = LoadIcon (hInstance, lpszAppName);
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
wc.lpszMenuName = lpszAppName;
wc.lpszClassName = lpszAppName;
if ( IS_WIN95 )
{
if ( !RegisterWin95( &wc ) )
return( FALSE );
}
else if ( !RegisterClass( &wc ) )
return( FALSE );
hInst = hInstance;
hWnd = CreateWindow( lpszAppName,
lpszTitle,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0,
CW_USEDEFAULT, 0,
NULL,
NULL,
hInstance,
NULL
);
if ( !hWnd )
return( FALSE );
ShowWindow( hWnd, nCmdShow );
UpdateWindow( hWnd );
while( GetMessage( &msg, NULL, 0, 0) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
return( msg.wParam );
}
BOOL RegisterWin95( CONST WNDCLASS* lpwc )
{
WNDCLASSEX wcex;
wcex.style = lpwc->style;
wcex.lpfnWndProc = lpwc->lpfnWndProc;
wcex.cbClsExtra = lpwc->cbClsExtra;
wcex.cbWndExtra = lpwc->cbWndExtra;
wcex.hInstance = lpwc->hInstance;
wcex.hIcon = lpwc->hIcon;
wcex.hCursor = lpwc->hCursor;
wcex.hbrBackground = lpwc->hbrBackground;
wcex.lpszMenuName = lpwc->lpszMenuName;
wcex.lpszClassName = lpwc->lpszClassName;
// Added elements for Windows 95.
//...............................
wcex.cbSize = sizeof(WNDCLASSEX);
wcex.hIconSm = LoadImage(wcex.hInstance, lpwc->lpszClassName,
IMAGE_ICON, 16, 16,
LR_DEFAULTCOLOR );
return RegisterClassEx( &wcex );
}
#define USR_SHORTMSG (WM_USER+1)
#define MSG_LEN 1024
#define DATABLOCK_SIZE 1024L
enum Status
{
StatusOn,
StatusError,
StatusOff,
StatusSendSysExcl,
} eStatus = StatusOff;
char msg[MSG_LEN+1];
HWND hListBox = NULL;
MMRESULT rc;
UINT nDevId = 0;
HMIDIOUT hmo = NULL;
MIDIHDR* pmh = NULL;
typedef struct
{
DWORD ms; // time in milliseconds
DWORD dwMidiMsg; // short MIDI message
} MIDIMSG, *LPMIDIMSG;
typedef struct
{
DWORD dwNextMsg; // next MIDI short message to play
DWORD ms; // current position in milliseconds
} MIDISONG, *LPMIDISONG;
#define SONG_LEN 6
MIDIMSG MidiMsg[SONG_LEN] = { 0, 0x403c90, // C on (C3)
200, 0x003c90, // C off
400, 0x404c90, // G on
600, 0x004c90, // G off
800, 0x404090, // E on
1000, 0x004090 // E off
};
MIDISONG MidiSong;
TIMECAPS tc;
UINT nTimerId;
UINT nTimerRes;
#define EXCL_LEN 6
BYTE ExclData[EXCL_LEN] = { 0xf0, // beginf system exclusive
0x41, // data
0x41,
0x00,
0x23,
0xf7 // EOX (End of Exclusive)
};
VOID CALLBACK timeProc(UINT nTimerId, UINT msg, DWORD dwUser, DWORD dwParam1, DWORD dwParam2);
LRESULT CALLBACK WndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{
switch( uMsg )
{
case WM_CREATE :
eStatus = StatusOff;
SetWindowText(hWnd, "MIDI Output - Off");
// allocate MIDIHDR buffer block
//..............................
pmh = HeapAlloc( GetProcessHeap(),
HEAP_ZERO_MEMORY,
sizeof(MIDIHDR) );
if (pmh)
{
pmh->lpData = HeapAlloc( GetProcessHeap(),
HEAP_ZERO_MEMORY,
DATABLOCK_SIZE);
pmh->dwBufferLength = DATABLOCK_SIZE;
}
else
return( -1 );
// create ListBox
//...............
hListBox = CreateWindow( "LISTBOX", "",
WS_CHILD | LBS_NOTIFY |
WS_VSCROLL | WS_BORDER |
WS_VISIBLE | LBS_NOINTEGRALHEIGHT,
0, 0,
0, 0,
hWnd,
(HMENU)101,
hInst,
NULL );
break;
case WM_SIZE :
MoveWindow( hListBox, 0, 0,
LOWORD( lParam ),
HIWORD( lParam ), TRUE );
break;
case WM_COMMAND :
switch( LOWORD( wParam ) )
{
case IDM_TEST:
{
MIDIOUTCAPS moc;
KEYARRAY DrumPatchCache;
KEYARRAY InstPatchCache;
if (eStatus == StatusOn || eStatus == StatusSendSysExcl)
break; // already busy
SendMessage(hListBox, LB_RESETCONTENT, 0, 0);
// open MIDI output device
//........................
rc = midiOutOpen(&hmo, nDevId, (DWORD)NULL,
(DWORD)NULL, CALLBACK_NULL);
if (rc != MMSYSERR_NOERROR)
{
midiOutGetErrorText(rc, msg, MSG_LEN);
MessageBox(hWnd, msg, NULL, MB_OK);
break;
}
rc = midiOutGetDevCaps(nDevId, &moc, sizeof(MIDIOUTCAPS));
if (moc.dwSupport & MIDICAPS_CACHE)
{
midiOutCacheDrumPatches(hmo, nDevId, DrumPatchCache, MIDI_CACHE_QUERY);
midiOutCachePatches (hmo, nDevId, InstPatchCache, MIDI_CACHE_QUERY);
}
if (moc.dwSupport & MIDICAPS_VOLUME)
{
DWORD dwVol;
// set volume level to at least 80%
//.................................
rc = midiOutGetVolume(hmo, &dwVol);
if (rc == MMSYSERR_NOERROR)
if (LOWORD(dwVol) < 0xCCCC ||
(moc.dwSupport & MIDICAPS_LRVOLUME &&
HIWORD(dwVol) < 0xCCCC ) )
rc = midiOutSetVolume(hmo, (DWORD)MAKELONG(0xCCCC, 0xCCCC) );
}
// display device capablilites
//............................
SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)moc.szPname);
sprintf(msg, "Voices:%d Notes:%d Channel Mask:%x",
moc.wVoices, moc.wNotes, moc.wChannelMask);
SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)msg);
SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)"");
SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)"Supports the following:");
// wTechnology
if (moc.wTechnology & MOD_MIDIPORT)
SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)" MIDI hardware port");
if (moc.wTechnology & MOD_SQSYNTH)
SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)" Square wave synthesizer");
if (moc.wTechnology & MOD_FMSYNTH)
SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)" FM synthesizer");
if (moc.wTechnology & MOD_MAPPER)
SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)" Microsoft MIDI Mapper");
// dwSupport
if (moc.dwSupport & MIDICAPS_VOLUME)
SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)" Volume control");
if (moc.dwSupport & MIDICAPS_LRVOLUME)
SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)" Separate left and right volume control");
if (moc.dwSupport & MIDICAPS_CACHE)
SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)" Patch caching");
// close MIDI output device
//.........................
if (hmo)
{
midiOutReset(hmo);
midiOutClose(hmo);
}
}
break;
case IDM_ON :
{
if (eStatus == StatusOn || eStatus == StatusSendSysExcl)
break; // already busy
// reset MIDI song struct
//.......................
MidiSong.dwNextMsg = 0;
MidiSong.ms = 0;
SendMessage(hListBox, LB_RESETCONTENT, 0, 0);
// setup high resolution MIDI timer
//.................................
rc = timeGetDevCaps(&tc, sizeof(TIMECAPS));
if (rc == TIMERR_NOERROR)
{
nTimerRes = min( max(tc.wPeriodMin, 1), tc.wPeriodMax);
timeBeginPeriod(nTimerRes);
nTimerId = timeSetEvent(nTimerRes, nTimerRes,
timeProc, (DWORD)hWnd,
TIME_PERIODIC);
}
if (rc != TIMERR_NOERROR || nTimerRes == 0)
{
MessageBox(hWnd, "Error setting up timer...", NULL, MB_OK);
break;
}
// open MIDI output device for playback
//.....................................
rc = midiOutOpen(&hmo, nDevId, (DWORD)NULL,
(DWORD)NULL, CALLBACK_NULL);
if (rc == MMSYSERR_NOERROR)
{
eStatus = StatusOn;
SetWindowText(hWnd, "MIDI Output - On");
}
else
{
midiOutGetErrorText(rc, msg, MSG_LEN);
MessageBox(hWnd, msg, NULL, MB_OK);
// kill timers
//............
timeKillEvent(nTimerId);
timeEndPeriod(nTimerRes);
break;
}
}
break;
case IDM_OFF :
{
if (eStatus != StatusOn)
break; // already off
// kill timers
//............
timeKillEvent(nTimerId);
timeEndPeriod(nTimerRes);
// shut down MIDI output
//......................
midiOutReset(hmo);
midiOutClose(hmo);
eStatus = StatusOff;
SetWindowText(hWnd, "MIDI Output - Off");
}
break;
case IDM_SENDSYSEXCL:
{
if (eStatus == StatusOn || eStatus == StatusSendSysExcl)
break; // already busy
SendMessage(hListBox, LB_RESETCONTENT, 0, 0);
// load system exclusive message into data buffer block
//.....................................................
memcpy(pmh->lpData, ExclData, EXCL_LEN);
pmh->dwBufferLength = EXCL_LEN;
// open MIDI output device for playback
//.....................................
rc = midiOutOpen(&hmo, nDevId, (DWORD)hWnd,
(DWORD)NULL, CALLBACK_WINDOW);
// prepare data buffer block and send to MIDI device
//..................................................
rc = midiOutPrepareHeader(hmo, pmh, sizeof(MIDIHDR));
if (rc == MMSYSERR_NOERROR)
rc = midiOutLongMsg(hmo, pmh, sizeof(MIDIHDR));
if (rc == MMSYSERR_NOERROR)
{
eStatus = StatusSendSysExcl;
SetWindowText(hWnd, "Sending MIDI system exclusive message");
}
else
{
midiOutGetErrorText(rc, msg, MSG_LEN);
MessageBox(hWnd, msg, NULL, MB_OK);
break;
}
}
break;
case IDM_ABOUT :
DialogBox( hInst, "AboutBox", hWnd, About );
break;
case IDM_EXIT :
DestroyWindow( hWnd );
break;
}
break;
case USR_SHORTMSG:
{
// display short message sent to the MIDI output device
//.....................................................
sprintf(msg, "Short Message: %x %x %x",
LOBYTE( LOWORD(lParam) ),
HIBYTE( LOWORD(lParam) ),
LOBYTE( HIWORD(lParam) ) );
SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)msg);
}
break;
case MM_MOM_DONE:
{
DWORD i;
char cTmp[33];
LPMIDIHDR lpMidiHdr = (LPMIDIHDR)lParam;
// check to see if the buffer is ready to be processed
//....................................................
if ( !(lpMidiHdr->dwFlags & MHDR_DONE) )
break;
// display the system exclusive MIDI message
//..........................................
if (lpMidiHdr->dwBufferLength > 0)
{
lstrcpy(msg, "System Exclusive: ");
for (i = 0; i < lpMidiHdr->dwBufferLength &&
lstrlen(msg) < MSG_LEN - 4; i++)
{
sprintf(cTmp, " %02x", lpMidiHdr->lpData[i]);
lstrcat(msg, cTmp);
}
SendMessage(hListBox, LB_ADDSTRING, 0, (LPARAM)msg);
}
// unprepeare the data buffer block
// and close the MIDI output device
//.................................
midiOutPrepareHeader(hmo, lpMidiHdr, sizeof(MIDIHDR));
midiOutReset(hmo);
midiOutClose(hmo);
eStatus = StatusOff;
SetWindowText(hWnd, "MIDI Output - Off");
}
break;
case WM_DESTROY :
// kill timers
//............
timeKillEvent(nTimerId);
timeEndPeriod(nTimerRes);
// shut down MIDI output
//......................
if (hmo)
{
midiOutReset(hmo);
//midiInUnprepareHeader(hmi, pmh, sizeof(MIDIHDR));
midiOutClose(hmo);
}
// free the MIDIHDR buffer block
//..............................
if (pmh != NULL)
{
HeapFree(GetProcessHeap(), 0, pmh->lpData);
HeapFree(GetProcessHeap(), 0, pmh);
pmh = NULL;
}
PostQuitMessage(0);
break;
default :
return( DefWindowProc( hWnd, uMsg, wParam, lParam ) );
}
return( 0L );
}
VOID CALLBACK timeProc(UINT nTimerId, UINT msg, DWORD dwUser, DWORD dwParam1, DWORD dwParam2)
{
DWORD i;
// send all MIDI short messages that match
// the current ms time
//........................................
for (i = MidiSong.dwNextMsg;
i < SONG_LEN && MidiSong.ms == MidiMsg[i].ms;
i++)
{
midiOutShortMsg(hmo, MidiMsg[i].dwMidiMsg);
MidiSong.dwNextMsg = i + 1;
PostMessage((HWND)dwUser, USR_SHORTMSG, 0, MidiMsg[i].dwMidiMsg);
}
MidiSong.ms++; // incr. the current time
// check if we have hit the end of the of the MIDI song
// if so, shut down the timer
//.....................................................
if (MidiSong.dwNextMsg >= SONG_LEN)
{
timeKillEvent(nTimerId);
PostMessage((HWND)dwUser, WM_COMMAND, IDM_OFF, 0);
}
}
LRESULT CALLBACK About( HWND hDlg,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
switch (message)
{
case WM_INITDIALOG:
return (TRUE);
case WM_COMMAND:
if ( LOWORD(wParam) == IDOK
|| LOWORD(wParam) == IDCANCEL)
{
EndDialog(hDlg, TRUE);
return (TRUE);
}
break;
}
return (FALSE);
}